﻿using System;
using System.Data;
using System.Text;
using System.Collections;
using System.Collections.Generic;

namespace BizElements.Core
{
    /// <summary>
    /// Select list. Specifies the items to be returned by the <see cref="SelectStatement"/>.
    /// </summary>
    /// <remarks>Strong-typed SqlItemCollection, may contain only SelectItem or inherited objects.</remarks>
    [Serializable]
    public sealed class SelectItemCollection : IEnumerable, IEnumerable<SelectItem>
    {
        List<SelectItem> list = new List<SelectItem>();

        #region CTor.

        /// <summary>
        /// Instantiates a new select list.
        /// </summary>
        public SelectItemCollection()
            : base()
        { 
        }

        #endregion

        #region Indexers and properties.

        /// <summary>
        /// Gets the item at specified position.
        /// </summary>
        public SelectItem this[int index]
        {
            get { return this.list[index]; }
        }

        /// <summary>
        /// Gets the item with the given alias. Null if it doesn't exist.
        /// </summary>
        public SelectItem this[string itemAlias]
        {
            get
            {
                foreach (SelectItem item in this.list)
                {
                    if (item.Alias == itemAlias)
                        return item;
                }

                return null;
            }
        }

        /// <summary>
        /// Gets the number of items in the collection.
        /// </summary>
        public int Count
        {
            get { return this.list.Count; }
        }

        #endregion

        #region Add(), Clear().

        /// <summary>
        /// Adds an item to the list.
        /// </summary>
        /// <param name="item">Any select item.</param>
        public void Add(SelectItem item)
        {
            if (item == null)
                throw new ArgumentNullException("item", Messages.SelectItemCollection_ObjRefIsNull);

            this.list.Add(item);
        }

        /// <summary>
        /// Adds items the list.
        /// </summary>
        /// <param name="items">Collection of select items.</param>
        /// <remarks>Adds all items in the given collection to this list.</remarks>
        public void Add(SelectItemCollection items)
        {
            foreach (SelectItem item in items)
                this.list.Add(item);
        }

        /// <summary>
        /// Adds an item to the list.
        /// </summary>
        /// <param name="field">Field.</param>
        public void Add(IDbColumn field)
        {
            Add(new SelectItem(field));
        }

        /// <summary>
        /// Adds items to the list.
        /// </summary>
        /// <param name="fields">Array of fields.</param>
        public void Add(params IDbColumn[] fields)
        {
            foreach (IDbColumn col in fields)
                Add(col);
        }

        /// <summary>
        /// Adds items to the list.
        /// </summary>
        /// <param name="fields">Collection of fields.</param>
        public void Add(DbColumnCollection fields)
        {
            foreach (IDbColumn field in fields)
                Add(new SelectItem(field));
        }

        /// <summary>
        /// Adds an item to the list.
        /// </summary>
        /// <param name="function">Function. Usually an <see cref="AggregateFunction"/>.</param>
        public void Add(Function function)
        {
            Add(new SelectItem(function));
        }

        /// <summary>
        /// Adds an item to the list.
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="alias">Query alias.</param>
        public void Add(SelectStatement subQuery, string alias)
        {
            if (alias != null && alias.Length > 0)
                subQuery.Alias = alias;

            Add(new SelectItem(subQuery));
        }

        /// <summary>
        /// Removes all items from the collection.
        /// </summary>
        public void Clear()
        {
            this.list.Clear();
        }

        #endregion

        #region IEnumerable.

        /// <summary>
        /// Gets an enumerator that can iterate through SqlItemCollection.
        /// </summary>
        public IEnumerator GetEnumerator()
        {
            return this.list.GetEnumerator();
        }

        #endregion

        #region IEnumerable<SelectItem> Members

        /// <summary>
        /// Gets an enumerator that can iterate through SqlItemCollection.
        /// </summary>
        IEnumerator<SelectItem> IEnumerable<SelectItem>.GetEnumerator()
        {
            return this.list.GetEnumerator();
        }

        #endregion

        #region SelectItemCollection specific methods.

        /// <summary>
        /// Determines whether the collection contains the element with the specified alias.
        /// </summary>
        /// <param name="itemAlias">Item alias.</param>
        /// <returns><b>true</b> if item with the specified alias exists in the collection; otherwise <b>false</b>.</returns>
        public bool ContainsItemWithAlias(string itemAlias)
        {
            return SqlItemUtil.ContainsItemWithAlias(this, itemAlias);
        }

        /// <summary>
        /// Gets all items of the given type.
        /// </summary>
        /// <param name="itemType">Item type.</param>
        /// <returns>Collection of items of the given type.</returns>
        public SelectItemCollection GetByType(SqlItemType itemType)
        {
            SelectItemCollection itemsWithSpecifiedType = new SelectItemCollection();
            foreach (SelectItem item in this.list)
            {
                if (item.ItemType == itemType)
                    itemsWithSpecifiedType.Add(item);
            }

            return itemsWithSpecifiedType;
        }

        #endregion

        #region Render.

        /// <summary>
        /// Renders SQL item as SQL statement.
        /// </summary>
        /// <param name="dbms">Target DBMS.</param>
        /// <param name="output">StringBuilder to which SQL is appended.</param>
        /// <param name="parameters">SQL parameter collection to which the object's and its children's
        /// parameters are added. After the rendering is done the collection contains all parameters with unique names.</param>
        public void Render(DbmsType dbms, StringBuilder output, DbParameterCollection parameters)
        {
            for (int itemIdx = 0; itemIdx < this.list.Count - 1; itemIdx++)
            {
                this.list[itemIdx].Render(dbms, output, parameters);
                output.Append(", ");
            }

            this.list[this.Count - 1].Render(dbms, output, parameters);
        }

        #endregion
    }
}